/*
 * Copyright © 2017 eqxiu.com 北京中网易企秀科技有限公司  All rights reserved.
 */

package cn.hermit.reflection.parameter;

import java.lang.reflect.AccessibleObject;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author JackGao 高建国
 * @description //TODO 设计说明
 * @date 1/6/2017
 */
public class CachingParameterNameFinder implements ParameterNameFinder {

    private final ParameterNameFinder delegate;

    private final Map<AccessibleObject,String[]> methodCache = makeMethodCache();

    protected Map<AccessibleObject, String[]> makeMethodCache() {
        return Collections.synchronizedMap(new WeakHashMap<AccessibleObject, String[]>());
    }

    /**
     * Uses a DefaultParameterNameFinder as the implementation it delegates to.
     */
    public CachingParameterNameFinder() {
        this(new AdaptiveParameterNameFinder());
    }

    /**
     * Specify a ParameterNameFinder instance to delegates to.
     * @param delegate the ParameterNameFinder instance to use
     */
    public CachingParameterNameFinder(ParameterNameFinder delegate) {
        this.delegate = delegate;
    }

    @Override
    public String[] findParameterNames(AccessibleObject methodOrConstructor) {
        return findParameterNames(methodOrConstructor, true);
    }

    @Override
    public String[] findParameterNames(AccessibleObject methodOrConstructor, boolean throwExceptionIfMissing) {
        String[] names = methodCache.get(methodOrConstructor);
        if(names == null) {
            names = delegate.findParameterNames(methodOrConstructor, throwExceptionIfMissing);
            methodCache.put(methodOrConstructor, names);
        }
        return names;
    }

    /**
     * This implementation has a better concurrent design (ConcurrentHashMap) which
     * has a better strategy to implement concurrency: segments instead of synchronized.
     *
     * It also drops the underlying WeakHashMap implementation as that can't work with
     * ConcurrentHashMap with some risk of growing permgen for a certain class of usage.
     *
     * So instead of wrapping via 'Collections.synchronizedMap(new WeakHashMap())' we now
     * have 'new ConcurrentHashMap()'
     *
     */
    public static class WithoutWeakReferences extends CachingParameterNameFinder {

        public WithoutWeakReferences() {
        }

        public WithoutWeakReferences(ParameterNameFinder delegate) {
            super(delegate);
        }

        @Override
        protected Map<AccessibleObject, String[]> makeMethodCache() {
            return new ConcurrentHashMap<AccessibleObject, String[]>();
        }
    }
}
